#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##-- G02 -- S02 ------------------------------------------------------------##
__all__ = []

from .. import pilelib as stack
from .. import stackfill as fill
from ..formlib import Layout
from ..gamelib import info as gi, state, game
from ..packlib import info as cs, place

GT, SL = gi.GT, gi.SL
StackWrapper = stack.StackWrapper


class Parallels_Talon(stack.DealRowTalon):
    """
    Deal 1 card on _row_ gap_ else last _row_.
    """

    @property
    def _cards(self):
        """
        _cards -> tup

        :return: tuple (max cards, min cards) in col of _piles_.
        :rtype: tuple
        """
        col = [sum(int(bool(r.cards)) for r in
               (self._piles_[i::10] for i in range(10)))]
        return min(col), max(col)

    def deal_cards(self, sound=False):
        """
        deal_cards([sound:bool=False]) -> None

        Actual dealing, called from Game.talon_deal(). Overides default,
        deal as _piles with no cards or new piles of _piles.

        :keyword boolean sound: sound fx?
        """
        for r in self._piles_[:10]:  # Fill top N row
            if not r.cards:
                return self._fillRow()
        min_cards, max_cards = self._cards
        if max_cards != min_cards:  # Fill gap row
            return self._fillRow()
        index = max_cards * 10
        return super(Parallels_Talon, self).dealRowAvail(  # Fill new S row
                                          rows=self._piles_[index:index + 10])

    def _fillRow(self):
        """
        _fillRow([sound:bool=False]) -> None

        Deal gaps in _piles.

        :keyword boolean sound: sound fx?
        """
        rows = self.game.s['rows']
        min_cards, max_cards = self._cards_
        max_cards = max(max_cards, 1)
        ncards = 0
        rows = rows[:max_cards * 10]
        while self.cards:  # Deal gap after next has cards
            for i in range(10):  # Col
                pile = []
                for s in rows[i::10]:  # Col of _row_
                    if not self.cards:
                        break
                    if s.cards:
                        if pile:
                            ncards += super(Parallels_Talon, self).dealRow(
                                                                    rows=pile)
                        break
                    pile = [s]
        while self.cards:  # Deal remaining gap
            for i in range(10):  # Col
                for s in rows[i::10]:  # Col of _row_
                    if not self.cards:
                        break
                    if not s.cards:
                        ncards += super(Parallels_Talon, self).dealRow(
                                                                  rows=[s])
                        break
        return ncards


class Parallels_RowStack(stack.BasicRowStack):
    """
    Build none. Play _foundation_ when on N or S edge. Play top when Gap N S
    reach edge.
    """

    def cardsBlock(self):
        """
        cardsBlock() -> bool

        Check if pile is blocked by seeing if piles are inner rows with cards
        N or S.

        :return: pile is blocked?
        :rtype: boolean
        """
        rows = self.game.s['rows']
        return not(not self in rows[10:] or not rows[self.id - 10].cards or
                   not self in rows[:-10] or not rows[self.id + 10].cards)


class Parallels(game.Game):
    """
    Finish cards on _foundation_ with a turn.

    _row_
      Pile 10 horz N. Init deal 1 card. Build none. Play _foundation_ when on
      N or S edge. Play top when Gap N S reach edge.

    _talon_
      Pile 1 NW. Deal 1 card on _row_ gap_ else last _row_.

    _foundation_
      Pile 8 over 2 rows. Init deal base ACE W; base KING E. Build up in rank
      by same suit. Play top.

    seealso::
      <http://en.wikipedia.org/wiki/Parallels_%28solitaire%29>
    ------
    :cvar Stack _talon_: Parallels_Talon
    :cvar Stack _foundation_: stack.SS_Foundation,
      stack.SS_Foundation (base_rank=KING, dir=-1)
    :cvar Stack _row_: Parallels_RowStack (max_accept=0)
    :cvar Stack _reserve_: None
    """
    __info__ = dict(id=428, name="Parallels", game_type=GT.TWO_DECK, decks=2,
                    redeals=0, skill_level=SL.BALANCED, version='0.91fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Parallels_Talon
    _foundations_ = (stack.SS_Foundation,
                     StackWrapper(stack.SS_Foundation, base_rank=cs.KING,
                                  dir=-1),)
    _row_ = StackWrapper(Parallels_RowStack, max_accept=0)

    def _create_game(self, rows=(10,) * 7, **kw):
        l, s = Layout(self), self.s
        self.table_size = (l.XM + (max(rows) + 2) * l.XS,
                            l.YM + len(rows) * l.YS,)
        s['talon'] = self._talon_(l.XM, l.YM, game=self)
        l.set_ncards_text(s['talon'], 's')
        dx, y = l.XM + l.XS, l.YM
        for row in rows:  # Vert
            x = dx
            for i in range(row):
                s['rows'].append(self._row_(x, y, game=self))
                x += l.XS
            y += l.YS
        x, dy = l.XM, (self.height - l.SUIT * l.YS) / 2
        for _foundation_ in self._foundations_:  # Horz
            y = dy
            for suit in range(l.SUIT):  # Vert
                s['foundations'].append(_foundation_(x, y, self, suit=suit))
                y += l.YS
            x += self.width - l.XS
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE and KING of suit on top by rank, suit, deck for
        _foundation_ deal.
        """
        return place.MoveToTop(cards, place.OneAceKing)

    def _start(self, **kw):
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        self.s['talon'].dealRow(rows=self.s['rows'][:10])


class BritishBlockade(Parallels):
    """
    Finish cards on _foundation_ with a turn.

    _row_
      Pile 10 horz N. Init deal 1 card. Build none. Play _foundation_ when on
      N or S edge. Gap deal 1 card from _talon_ Play top when Gap N S reach
      edge.

    _talon_
      Pile 1 NW. Deal 1 card on a _row_ gap_ else _row_.

    _foundation_
      Pile 8 over 2 rows. Init deal base ACE W; base KING E. Build up in rank
      by same suit. Play top.
    ------
    :cvar Stack _talon_: Parallels_Talon
    :cvar Stack _foundation_: stack.SS_Foundation,
      stack.SS_Foundation (base_rank=KING, dir=-1)
    :cvar Stack _row_: Parallels_RowStack (max_accept=0)
    :cvar Stack _reserve_: None
    """
    __info__ = dict(id=615, name="British Blockade", game_type=GT.TWO_DECK,
                    decks=2, redeals=0, skill_level=SL.BALANCED,
                    version='0.91fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _talon_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromTalon(self, stack, self.s['rows'])


PROVIDES = [Parallels, BritishBlockade]